package edu.mtu.citizenscience.ethnography.uploader;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.json.JSONException;
import org.json.JSONObject;

import edu.mtu.citizenscience.ethnography.database.Interview;
import edu.mtu.citizenscience.ethnography.database.Interview.Name;
import edu.mtu.citizenscience.ethnography.database.InterviewAudio;
import edu.mtu.citizenscience.ethnography.database.InterviewDataService;
import edu.mtu.citizenscience.ethnography.database.InterviewDataService.InterviewBinder;
import edu.mtu.citizenscience.ethnography.database.InterviewPhoto;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

public class EthnographyUploadAction implements EthnographyUploaderAction {

	private String LOG_TAG = "EthnographyUploadAction";

	private boolean isInitialized = false;

	private EthnographyUploadHandler handler;

	private Interview interview = null;
	private List<InterviewAudio> audio = null;
	private int audioPos = 0;
	private List<InterviewPhoto> photos = null;
	private int photoPos = 0;
	private boolean fileMode = false; // false when we're uploading the file metadata, true to upload file itself

	private Context context;

	private InterviewDataService dataService = null;

	private boolean bound;

	private ServiceConnection connection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			InterviewBinder binder = (InterviewBinder) service;
			dataService = binder.getService();
			bound = true;
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.e(LOG_TAG, "Interview disconnected unexpectedly");
			bound = false;
		}

	};

	// resource names
	private String interviewResource = "Story_API/story";
	private String audioResource = "Storyaudio_API/StoryAudio";
	private String audioFileResource = audioResource + "Upload";
	private String photoResource = "Storyphoto_API/StoryPhoto";
	private String photoFileResource = photoResource + "Upload";

	private boolean hasNext = true;

	// Interview Key names
	private static final String SIN_THEME = "ThemeID";
	private static final String SIN_TITLE = "StoryTitle";
	private static final String SIN_TEXT = "StoryText";
	private static final String SIN_LOCATION = "Location";
	private static final String SIN_DATE = "Date";
	private static final String SIN_STNAME = "StoryTellerName";
	private static final String SIN_SBNAME = "SubjectName";

	static final String SOUT_ID = "id";

	// Media key names
	private static final String MIN_ID = "StoryID";
	private static final String MIN_DATE = "Date";
	private static final String MIN_ST_LAT = "StartLatitude";
	private static final String MIN_ST_LONG = "StartLongitude";
	private static final String MIN_END_LAT = "EndLatitude";
	private static final String MIN_END_LONG = "EndLongitude";
	private static final String MIN_LAT = "Latitude";
	private static final String MIN_LONG = "Longitude";
	private static final String MIN_FILENAME = "Filename";

	// File stream key
	static final String FILE = "file";
	static final String FILENAME = "filename";

	public EthnographyUploadAction(Context context) {
		this.context = context;
		handler = new EthnographyUploadHandler();
	}

	@Override
	public Map<String, Object> getParams(Intent intent) {
		if (!isInitialized) {
			
			int interviewID = intent.getIntExtra(EthnographyUploadService.INPUT_ID, -1);
			if (interviewID == -1) {
				return null;
			}
			context.bindService(new Intent(context, InterviewDataService.class), connection, Context.BIND_AUTO_CREATE);

			while (!bound)
				;
			interview = dataService.getInterview(interviewID);
			if (interview == null) {
				context.unbindService(connection);
				bound = false;
				return null;
			}

			// Get the audio list, but remove any already-uploaded ones
			audio = dataService.audioFilesForInterview(interview);
			Iterator<InterviewAudio> aitr = audio.iterator();

			while (aitr.hasNext()) {
				InterviewAudio a = aitr.next();
				if (a.isUploaded()) {
					aitr.remove();
				}
			}

			// Do the same thing with the photo list
			photos = dataService.photoFilesForInterview(interview);
			Iterator<InterviewPhoto> pitr = photos.iterator();

			while (pitr.hasNext()) {
				InterviewPhoto p = pitr.next();
				if (p.isUploaded()) {
					pitr.remove();
				}
			}

			context.unbindService(connection);
			handler.sendStatusMessage();
			bound = false;
			isInitialized = true;
		}

		Map<String, Object> map = null;

		if (!interview.isUploaded()) {
			map = new HashMap<String, Object>();

			Name subjectName = interview.getSubjectName();
			map.put(SIN_SBNAME, subjectName.getFullName());

			Name interviewerName = interview.getInterviewerName();
			map.put(SIN_STNAME, interviewerName.getFirstName());

			map.put(SIN_LOCATION, interview.getLocationName());

			DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd", context.getResources().getConfiguration().locale);
			String dateStr = dateFormatter.format(interview.getDate());
			map.put(SIN_DATE, dateStr);

			map.put(SIN_TEXT, interview.getNotes());
			map.put(SIN_TITLE, interview.getStoryTitle());
			map.put(SIN_THEME, 0);
		} else if (audioPos < audio.size()) {
			
			map = new HashMap<String, Object>();
			InterviewAudio currentAudio = audio.get(audioPos);
			if (!fileMode) {
				Log.d(LOG_TAG, String.format("Uploading metadata for audio file %d out of %d, server key %d", 
						audioPos + 1, audio.size(), interview.getServerKey()));
				map.put(MIN_ID, interview.getServerKey());

				DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", context.getResources().getConfiguration().locale);
				String dateStr = dateFormatter.format(interview.getDate());
				map.put(MIN_DATE, dateStr);

				Location startLocation = currentAudio.getStartLocation();
				if (startLocation != null) {
					map.put(MIN_ST_LAT, startLocation.getLatitude());
					map.put(MIN_ST_LONG, startLocation.getLongitude());
				} else {
					map.put(MIN_ST_LAT, null);
					map.put(MIN_ST_LONG, null);
				}

				Location endLocation = currentAudio.getEndLocation();
				if (endLocation != null) {
					map.put(MIN_END_LAT, endLocation.getLatitude());
					map.put(MIN_END_LONG, endLocation.getLongitude());
				} else {
					map.put(MIN_END_LAT, null);
					map.put(MIN_END_LONG, null);
				}

				String filename = interview.getServerKey() + "_" + currentAudio.getAudioFile().getName();
				map.put(MIN_FILENAME, filename);
			} else {
				Log.d(LOG_TAG, String.format("Uploading data for audio file %d out of %d, server key %d", 
						audioPos + 1, audio.size(), interview.getServerKey()));
				File file = currentAudio.getAudioFile();
				if (file == null) {
					return null;
				}

				String filename = interview.getServerKey() + "_" + file.getName();

				map.put(FILE, file);
				map.put(FILENAME, filename);
				map.put(SOUT_ID, interview.getServerKey());
			}
		} else if (photoPos < photos.size()) {
			map = new HashMap<String, Object>();
			InterviewPhoto currentPhoto = photos.get(photoPos);
			if (!fileMode) {
				Log.d(LOG_TAG, String.format("Uploading metadata for photo file %d out of %d, server key %d", 
						photoPos + 1, photos.size(), interview.getServerKey()));
				map.put(MIN_ID, interview.getServerKey());

				DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", context.getResources().getConfiguration().locale);
				String dateStr = dateFormatter.format(interview.getDate());
				map.put(MIN_DATE, dateStr);

				Location location = currentPhoto.getLocation();
				if (location != null) {
					map.put(MIN_LAT, location.getLatitude());
					map.put(MIN_LONG, location.getLongitude());
				} else {
					map.put(MIN_LAT, null);
					map.put(MIN_LONG, null);
				}

				String filename = interview.getServerKey() + "_" + currentPhoto.getPhotoFile().getName();
				map.put(MIN_FILENAME, filename);
			} else {
				Log.d(LOG_TAG, String.format("Uploading data for photo file %d out of %d, server key %d", 
						photoPos + 1, photos.size(), interview.getServerKey()));
				File file = currentPhoto.getPhotoFile();
				if (file == null) {
					return null;
				}

				String filename = interview.getServerKey() + "_" + file.getName();

				map.put(FILE, file);
				map.put(FILENAME, filename);
				map.put(SOUT_ID, interview.getServerKey());
			}
		}

		return map;
	}

	@Override
	public String getResource() {
		String resource = null;
		if (!interview.isUploaded()) {
			resource = interviewResource;
		} else if (audioPos < audio.size()) {
			if (!fileMode) {
				resource = audioResource;
			} else {
				resource = audioFileResource;
			}
		} else if (photoPos < photos.size()) {
			if (!fileMode) {
				resource = photoResource;
			} else {
				resource = photoFileResource;
			}
		}
		return resource;
	}

	@Override
	public EthnographyResponseHandler getHandler() {
		return handler;
	}

	private class EthnographyUploadHandler implements
	EthnographyResponseHandler {

		LocalBroadcastManager manager;

		private EthnographyUploadHandler() {
			manager = LocalBroadcastManager.getInstance(context);;
		}

		@Override
		public void onSuccess(JSONObject json) {
			if (!interview.isUploaded()) {
				// process Interview response
				int serverID;
				try {
					serverID = json.getInt(SOUT_ID);
				} catch (JSONException e) {
					onError(null);
					return;
				}
				interview.setServerKey(serverID);
				interview.setUploaded(true);
				Intent intent = new Intent(context, InterviewDataService.class);
				context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
				while (!bound)
					; // XXX This is terrible but it works
				dataService.saveInterviewWithoutRename(interview);
				context.unbindService(connection);
				bound = false;
				
				Log.d(LOG_TAG, "Successfully uploaded interview");
			} else if (audioPos < audio.size()) {
				if (!fileMode) {
					fileMode = true;
					Log.d(LOG_TAG, "Successfully uploaded audio table " + audioPos + " out of " + audio.size());
				} else {
					InterviewAudio currentAudio = audio.get(audioPos);
					currentAudio.setUploaded(true);

					Intent intent = new Intent(context, InterviewDataService.class);
					context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
					while (!bound)
						; // XXX This is terrible but it works
					dataService.saveAudioFile(currentAudio);
					context.unbindService(connection);
					bound = false;

					fileMode = false;
					Log.d(LOG_TAG, "Successfully uploaded audio file " + audioPos + " out of " + audio.size());
					
					audioPos++;
				}
			} else if (photoPos < photos.size()) {
				if (!fileMode) {
					fileMode = true;
					Log.d(LOG_TAG, "Successfully uploaded photo table " + photoPos + " out of " + photos.size());
				} else {
					InterviewPhoto currentPhoto = photos.get(photoPos);
					currentPhoto.setUploaded(true);

					Intent intent = new Intent(context, InterviewDataService.class);
					context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
					while (!bound)
						; // XXX This is terrible but it works
					dataService.savePhotoFile(currentPhoto);
					context.unbindService(connection);
					bound = false;

					fileMode = false;
					Log.d(LOG_TAG, "Successfully uploaded photo file " + photoPos + " out of " + photos.size());
					
					photoPos++;
				}
			} 
			
			sendStatusMessage();

			hasNext = !(interview.isUploaded() && audioPos == audio.size() && photoPos == photos.size());

		}
		
		private void sendStatusMessage() {
			Intent broadcastIntent = new Intent(EthnographyUploadService.UPLOAD_SERVICE);
			
			if (!interview.isUploaded()) {
				broadcastIntent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_CONTINUE);
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_SENDTYPE, EthnographyUploadService.SENDTYPE_INTERVIEW);
			} else if (audioPos < audio.size()){
				broadcastIntent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_CONTINUE);
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_SENDTYPE, EthnographyUploadService.SENDTYPE_AUDIO);
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_TOTAL, audio.size());
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_CURRENT, audioPos + 1);
			} else if (photoPos < photos.size()) {
				broadcastIntent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_CONTINUE);
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_SENDTYPE, EthnographyUploadService.SENDTYPE_AUDIO);
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_TOTAL, photos.size());
				broadcastIntent.putExtra(EthnographyUploadService.CONTINUE_CURRENT, photoPos + 1);
			} else {
				broadcastIntent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_SUCCESS);
			}
			
			manager.sendBroadcast(broadcastIntent);
		}

		@Override
		public void onError(JSONObject json) {
			Intent intent = new Intent(EthnographyUploadService.UPLOAD_SERVICE);
			intent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_ERROR);
			manager.sendBroadcast(intent);
			hasNext = false;
		}

		@Override
		public void onFailure(JSONObject json) {
			Intent intent = new Intent(EthnographyUploadService.UPLOAD_SERVICE);
			intent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_FAILURE);
			manager.sendBroadcast(intent);
			hasNext = false;
		}

		@Override
		public void onNoResponse(JSONObject json) {
			Intent intent = new Intent(EthnographyUploadService.UPLOAD_SERVICE);
			intent.putExtra(EthnographyUploadService.OUTPUT_RESULT, EthnographyUploadService.RESULT_NO_RESPONSE);
			manager.sendBroadcast(intent);
			hasNext = false;
		}

	}

	@Override
	public boolean hasNext() {
		return hasNext;
	}

}
